package com.spring.theory.bean.beanFactory;

import static com.spring.theory.constant.GeneralConstant.LOG_INFO;

import java.io.IOException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
 * 阅读1-2
 * ApplicationContext的实现
 * 以及几种使用形式
 * <p>
 * ApplicationContext在BeanFactory接口基础上扩展了
 * - EnvironmentCapable（环境变量）
 * - MessageSource（国际化）
 * - ApplicationEventPublisher（事件发布）
 * - ResourcePatternResolver（通配符资源获取）
 *
 * @author 24373
 * @date 2023/12/23 16:33
 */

@Slf4j
public class TestApplicationContext {

    public static void main(String[] args) throws IOException {
        //xmlBean();
        annotationBean();
        //webAnnotationBean();
    }

    /**
     * 基于xml文件方式创建
     */
    private static void xmlBean() {
        /**
         * 1.xml文件注入bean
         */
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("TestBean.xml");
        log.info(LOG_INFO);
        Arrays.stream(context.getBeanDefinitionNames()).forEach(t -> log.info("BeanDefinitionNames:{}", t));
        log.info(LOG_INFO);
        Bean4 bean4 = context.getBean(Bean3.class).getBean4();
        log.info("bean4:{}", bean4);
        log.info(LOG_INFO);

        /**
         * 2.xml注入bean的内部原理；
         * ApplicationContext本质也是通过 BeanFactory 来实现
         */
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(new ClassPathResource("TestBean.xml"));
        Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(t -> log.info("BeanDefinitionNames:{}", t));
        Bean4 bean4_2 = beanFactory.getBean(Bean3.class).getBean4();
        log.info("{}", bean4_2);

    }

    /**
     * 基于注解配置的方式创建
     */
    private static void annotationBean() throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContextConfig.class);
        /**
         * ApplicationContext四大扩展接口
         */
        // ResourcePatternResolver 加载资源文件；*标识通配符，可以到jar包里面去查找
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        Arrays.stream(resources).forEach(t -> log.info("资源文件:{}", t));
        // 环境变量的获取；来自配置文件或者本地环境变量
        String property = context.getEnvironment().getProperty("java.version");

        Arrays.stream(context.getBeanDefinitionNames()).forEach(t -> log.info("BeanDefinitionNames:{}", t));
        log.info(LOG_INFO);
        Bean4 bean4 = context.getBean(Bean3.class).getBean4();
        log.info("{}", bean4);

    }

    /**
     * 基于配置类，用于web环境
     */
    private static void webAnnotationBean() {
        new AnnotationConfigServletWebServerApplicationContext(ApplicationContextWebConfig.class);
    }


    @Configuration
    static class ApplicationContextWebConfig {
        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }

        @Bean
        public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
            //"/"标识所有请求先经由此前置路由器
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        //约定以 / 为开头时是做web接口使用的
        @Bean("/hello")
        public Controller controller() {
            return new Controller() {
                @Override
                public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                    response.getWriter().println("hello");
                    return null;
                }
            };
        }

    }

    @Configuration
    static class ApplicationContextConfig {
        @Bean
        public Bean3 bean33(Bean4 bean4) {
            Bean3 bean3 = new Bean3();
            bean3.setBean4(bean4);
            return bean3;
        }

        @Bean
        public Bean4 bean44() {
            return new Bean4();
        }

    }

    static class Bean3 {
        private Bean4 bean4;

        public Bean3() {
            log.info("bean3 构造器");
        }

        public Bean4 getBean4() {
            log.info("bean4 获取");
            return this.bean4;
        }

        public void setBean4(Bean4 bean4) {
            log.info("bean4 注入");
            this.bean4 = bean4;
        }
    }

    static class Bean4 {
        public Bean4() {
            log.info("bean4 构造器");
        }
    }

}
